package com.open.capacity.server.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.open.capacity.annotation.log.LogAnnotation;
import com.open.capacity.server.service.ExtClientDetailsService;
import com.open.capacity.utils.SpringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 作者 owen E-mail: 624191343@qq.com
 * @version 创建时间：2018年4月28日 下午2:18:54 类说明
 */

@Api(tags = "OAuth2相关操作")
@RestController
public class OAuth2Controller {

    private static final Logger logger = LoggerFactory.getLogger(OAuth2Controller.class);
    @Resource
    private ObjectMapper objectMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtTokenStore jwtTokenStore;

    @ApiOperation(value = "用户名密码获取token")
    @PostMapping("/oauth/user/token")
    public void getUserTokenInfo(
            @ApiParam(required = true, name = "username", value = "账号") @RequestParam(value = "username") String username,
            @ApiParam(required = true, name = "password", value = "密码") @RequestParam(value = "password") String password,
            HttpServletRequest request, HttpServletResponse response) {
        String clientId = request.getHeader("client_id");
        String clientSecret = request.getHeader("client_secret");

        try {

            if (clientId == null || "".equals(clientId)) {
                throw new UnapprovedClientAuthenticationException("请求头中无client_id信息");
            }

            if (clientSecret == null || "".equals(clientSecret)) {
                throw new UnapprovedClientAuthenticationException("请求头中无client_secret信息");
            }

            ExtClientDetailsService clientDetailsService = SpringUtil.getBean(ExtClientDetailsService.class);

            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

            if (clientDetails == null) {
                throw new UnapprovedClientAuthenticationException("clientId对应的信息不存在");
            } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
                throw new UnapprovedClientAuthenticationException("clientSecret不匹配");
            }

            TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(),
                    "customer");

            OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);

            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);

            AuthenticationManager authenticationManager = SpringUtil.getBean(AuthenticationManager.class);

            Authentication authentication = authenticationManager.authenticate(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);

            OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);

            AuthorizationServerTokenServices authorizationServerTokenServices = SpringUtil
                    .getBean("defaultAuthorizationServerTokenServices", AuthorizationServerTokenServices.class);

            OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices
                    .createAccessToken(oAuth2Authentication);

            oAuth2Authentication.setAuthenticated(true);

            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(oAuth2AccessToken));
            response.getWriter().flush();
            response.getWriter().close();

        } catch (Exception e) {

            response.setStatus(HttpStatus.UNAUTHORIZED.value());

            response.setContentType("application/json;charset=UTF-8");

            Map<String, String> rsp = new HashMap<>();
            rsp.put("resp_code", HttpStatus.UNAUTHORIZED.value() + "");
            rsp.put("rsp_msg", e.getMessage());

            try {
                response.getWriter().write(objectMapper.writeValueAsString(rsp));
                response.getWriter().flush();
                response.getWriter().close();
            } catch (JsonProcessingException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }

        }
    }


    @ApiOperation(value = "clientId获取token")
    @PostMapping("/oauth/client/token")
    public void getClientTokenInfo(HttpServletRequest request, HttpServletResponse response) {

        String clientId = request.getHeader("client_id");
        String clientSecret = request.getHeader("client_secret");
        try {

            if (clientId == null || "".equals(clientId)) {
                throw new UnapprovedClientAuthenticationException("请求参数中无clientId信息");
            }

            if (clientSecret == null || "".equals(clientSecret)) {
                throw new UnapprovedClientAuthenticationException("请求参数中无clientSecret信息");
            }

            ExtClientDetailsService clientDetailsService = SpringUtil.getBean(ExtClientDetailsService.class);

            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

            if (clientDetails == null) {
                throw new UnapprovedClientAuthenticationException("clientId对应的信息不存在");
            } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
                throw new UnapprovedClientAuthenticationException("clientSecret不匹配");
            }

            Map<String, String> map = new HashMap<>();
            map.put("client_secret", clientSecret);
            map.put("client_id", clientId);
            map.put("grant_type", "client_credentials");
            TokenRequest tokenRequest = new TokenRequest(map, clientId, clientDetails.getScope(), "client_credentials");

            OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);

            AuthorizationServerTokenServices authorizationServerTokenServices = SpringUtil
                    .getBean("defaultAuthorizationServerTokenServices", AuthorizationServerTokenServices.class);
            OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);
            ClientCredentialsTokenGranter clientCredentialsTokenGranter = new ClientCredentialsTokenGranter(
                    authorizationServerTokenServices, clientDetailsService, requestFactory);

            clientCredentialsTokenGranter.setAllowRefresh(true);
            OAuth2AccessToken oAuth2AccessToken = clientCredentialsTokenGranter.grant("client_credentials",
                    tokenRequest);

            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(oAuth2AccessToken));
            response.getWriter().flush();
            response.getWriter().close();

        } catch (Exception e) {

            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setContentType("application/json;charset=UTF-8");
            Map<String, String> rsp = new HashMap<>();
            rsp.put("resp_code", HttpStatus.UNAUTHORIZED.value() + "");
            rsp.put("rsp_msg", e.getMessage());

            try {
                response.getWriter().write(objectMapper.writeValueAsString(rsp));
                response.getWriter().flush();
                response.getWriter().close();
            } catch (JsonProcessingException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }

        }
    }

    @ApiOperation(value = "access_token刷新token")
    @PostMapping(value = "/oauth/refresh/token", params = "access_token")
    public void refreshTokenInfo(String access_token, HttpServletRequest request, HttpServletResponse response) {

        // 拿到当前用户信息
        try {
            Authentication user = SecurityContextHolder.getContext().getAuthentication();

            if (user != null) {
                if (user instanceof OAuth2Authentication) {
                    Authentication athentication = (Authentication) user;
                    OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) athentication.getDetails();
                }

            }
            OAuth2AccessToken accessToken = jwtTokenStore.readAccessToken(access_token);
            OAuth2Authentication auth = (OAuth2Authentication) user;
            ExtClientDetailsService clientDetailsService = SpringUtil.getBean(ExtClientDetailsService.class);

            ClientDetails clientDetails = clientDetailsService
                    .loadClientByClientId(auth.getOAuth2Request().getClientId());

            AuthorizationServerTokenServices authorizationServerTokenServices = SpringUtil
                    .getBean("defaultAuthorizationServerTokenServices", AuthorizationServerTokenServices.class);
            OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);

            RefreshTokenGranter refreshTokenGranter = new RefreshTokenGranter(authorizationServerTokenServices,
                    clientDetailsService, requestFactory);

            Map<String, String> map = new HashMap<>();
            map.put("grant_type", "refresh_token");
            map.put("refresh_token", accessToken.getRefreshToken().getValue());
            TokenRequest tokenRequest = new TokenRequest(map, auth.getOAuth2Request().getClientId(),
                    auth.getOAuth2Request().getScope(), "refresh_token");

            OAuth2AccessToken oAuth2AccessToken = refreshTokenGranter.grant("refresh_token", tokenRequest);

            jwtTokenStore.removeAccessToken(accessToken);

            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(oAuth2AccessToken));
            response.getWriter().flush();
            response.getWriter().close();
        } catch (Exception e) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setContentType("application/json;charset=UTF-8");
            Map<String, String> rsp = new HashMap<>();
            rsp.put("resp_code", HttpStatus.UNAUTHORIZED.value() + "");
            rsp.put("rsp_msg", e.getMessage());

            try {
                response.getWriter().write(objectMapper.writeValueAsString(rsp));
                response.getWriter().flush();
                response.getWriter().close();
            } catch (JsonProcessingException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }

    }

    /**
     * 移除access_token和refresh_token
     *
     * @param access_token
     */
    @ApiOperation(value = "移除token")
    @DeleteMapping(value = "/oauth/remove/token", params = "access_token")
    public void removeToken(String access_token) {

        // 拿到当前用户信息
        Authentication user = SecurityContextHolder.getContext().getAuthentication();

        if (user != null) {
            if (user instanceof OAuth2Authentication) {
                Authentication athentication = (Authentication) user;
                OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) athentication.getDetails();
            }

        }
        OAuth2AccessToken accessToken = jwtTokenStore.readAccessToken(access_token);
        if (accessToken != null) {
            // 移除access_token
            jwtTokenStore.removeAccessToken(accessToken);

            // 移除refresh_token
            if (accessToken.getRefreshToken() != null) {
                jwtTokenStore.removeRefreshToken(accessToken.getRefreshToken());
            }

        }
    }

    @ApiOperation(value = "获取token信息")
    @PostMapping(value = "/oauth/get/token", params = "access_token")
    public OAuth2AccessToken getTokenInfo(String access_token) {

        // 拿到当前用户信息
        Authentication user = SecurityContextHolder.getContext().getAuthentication();

        if (user != null) {
            if (user instanceof OAuth2Authentication) {
                Authentication athentication = (Authentication) user;
                OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) athentication.getDetails();
            }

        }
        OAuth2AccessToken accessToken = jwtTokenStore.readAccessToken(access_token);

        return accessToken;

    }

    /**
     * 当前登陆用户信息
     * security获取当前登录用户的方法是SecurityContextHolder.getContext().getAuthentication()
     * 这里的实现类是org.springframework.security.oauth2.provider.OAuth2Authentication
     *
     * @return
     */
    @ApiOperation(value = "当前登陆用户信息")
    @RequestMapping(value = {"/oauth/userinfo"}, produces = "application/json") // 获取用户信息。/auth/user
    public Map<String, Object> getCurrentUserDetail() {
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("user", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
        logger.debug("认证详细信息:" + SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());


        //userInfo.put("authorities", AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities()));

        userInfo.put("resp_code", "200");

        logger.info("返回信息:{}", userInfo);

        return userInfo;
    }
}
